﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ProjectEuler.UI
{
    internal sealed class ProblemRunResult
    {
        private readonly ProblemRunStatus _status;
        private readonly ProblemStatus _worst;
        private readonly Int32 _total;
        
        private readonly Int32 _correct;
        private readonly Int32 _failed;
        private readonly Int32 _inconclusive;
        private readonly Int32 _wrong;

        private readonly TimeSpan _elapsed;

        public ProblemRunResult(IList<ProblemInfo> problems)
        {
            _total = problems.Count;
            _worst = GetWorstResult(problems);

            _elapsed = problems
                .Where(p => p.Elapsed.HasValue)
                .Aggregate(TimeSpan.Zero, (sum, p) => sum.Add(p.Elapsed.Value));

            var groups = problems
                .GroupBy(p => p.CurrentStatus)
                .ToDictionary(g => g.Key, g => g.Count());

            groups.TryGetValue(ProblemStatus.Correct, out _correct);
            groups.TryGetValue(ProblemStatus.Failed, out _failed);
            groups.TryGetValue(ProblemStatus.Inconclusive, out _inconclusive);
            groups.TryGetValue(ProblemStatus.Wrong, out _wrong);
        }

        private ProblemRunResult(ProblemRunStatus status)
        {
            _status = status;
        }

        public ProblemStatus WorstResult
        {
            get { return _worst; }
        }

        public Int32 CorrectAnswers
        {
            get { return _correct; }
        }

        public Int32 FailedAnswers
        {
            get { return _failed; }
        }

        public Int32 InconclusiveAnswers
        {
            get { return _inconclusive; }
        }

        public Int32 WrongAnswers
        {
            get { return _wrong; }
        }

        public Int32 TotalAnswers
        {
            get { return _total; }
        }

        public TimeSpan Elapsed
        {
            get { return _elapsed; }
        }

        public override String ToString()
        {
            switch (_status)
            {
                case ProblemRunStatus.Executing:
                    return Resources.InfoBox_ProblemRunInProgress;

                case ProblemRunStatus.Stopping:
                    return Resources.InfoBox_ProblemRunStopping;

                case ProblemRunStatus.Stopped:
                    return Resources.InfoBox_ProblemRunStopped;
            }

            var result = FormatResult(WorstResult);

            switch (WorstResult)
            {
                case ProblemStatus.Wrong:
                case ProblemStatus.Inconclusive:
                case ProblemStatus.Failed:
                    result.Append(' ');
                    result.AppendFormat(Resources.InfoBox_CorrectResultCount, _correct, _total);
                    break;
            }

            result.Append(' ');
            result.AppendFormat(Resources.InfoBox_Elapsed, _elapsed.TotalSeconds);

            return result.ToString();
        }

        private StringBuilder FormatResult(ProblemStatus s)
        {
            var result = new StringBuilder();

            switch (s)
            {
                case ProblemStatus.Correct:
                    result.AppendFormat(Resources.InfoBox_Correct, CorrectAnswers);
                    break;

                case ProblemStatus.Wrong:
                    result.AppendFormat(Resources.InfoBox_Wrong, WrongAnswers);
                    break;

                case ProblemStatus.Inconclusive:
                    result.AppendFormat(Resources.InfoBox_Inconclusive, InconclusiveAnswers);
                    break;

                case ProblemStatus.Failed:
                    result.AppendFormat(Resources.InfoBox_Failed, FailedAnswers);
                    break;
            }

            return result;
        }

        private static ProblemStatus GetWorstResult(IEnumerable<ProblemInfo> problems)
        {
            var worst = ProblemStatus.Correct;

            foreach (var p in problems)
            {
                if (p.CurrentStatus == ProblemStatus.Failed)
                {
                    return ProblemStatus.Failed;
                }

                if (p.CurrentStatus == ProblemStatus.Wrong)
                {
                    worst = ProblemStatus.Wrong;
                }

                if (p.CurrentStatus == ProblemStatus.Inconclusive)
                {
                    if (worst != ProblemStatus.Wrong)
                    {
                        worst = ProblemStatus.Inconclusive;
                    }
                }
            }

            return worst;
        }

        public static readonly ProblemRunResult Stopped = new ProblemRunResult(ProblemRunStatus.Stopped);
        public static readonly ProblemRunResult Stopping = new ProblemRunResult(ProblemRunStatus.Stopping);
        public static readonly ProblemRunResult InProgress = new ProblemRunResult(ProblemRunStatus.Executing);
    }
}
